import 'package:intl/intl.dart';

enum RawPhase {
  Menstruation,
  FertilePre,
  Ovulation,
  FertilePost,
  Follicular,
  Luteal,
}

enum DisplayPhase {
  Menstruation,
  Fertile, // 合并前后窗
  Ovulation,
  Follicular,
  Luteal,
}

class CycleDay {
  final DateTime date; // 精确到日
  final int dayIndex; // 1-based
  final RawPhase rawPhase;
  final DisplayPhase displayPhase;
  final bool rawTruncatedFromPrevCycle; // raw window 的前窗是否越界到上个周期（即被截断）
  final bool rawTruncatedToNextCycle; // raw 后窗是否越界到下个周期（即被截断）
  final bool overlappedByMenstruation; // raw 为 Fertile，但显示为 Menstruation（被覆盖）

  CycleDay({
    required this.date,
    required this.dayIndex,
    required this.rawPhase,
    required this.displayPhase,
    this.rawTruncatedFromPrevCycle = false,
    this.rawTruncatedToNextCycle = false,
    this.overlappedByMenstruation = false,
  });

  String rawPhaseLabel() {
    switch (rawPhase) {
      case RawPhase.Menstruation:
        return '月经期';
      case RawPhase.FertilePre:
        return '易孕前窗';
      case RawPhase.Ovulation:
        return '排卵日';
      case RawPhase.FertilePost:
        return '易孕后窗';
      case RawPhase.Follicular:
        return '卵泡期';
      case RawPhase.Luteal:
        return '黄体期';
    }
  }

  String displayPhaseLabel() {
    switch (displayPhase) {
      case DisplayPhase.Menstruation:
        return '月经期';
      case DisplayPhase.Fertile:
        return '易孕期';
      case DisplayPhase.Ovulation:
        return '排卵日';
      case DisplayPhase.Follicular:
        return '卵泡期';
      case DisplayPhase.Luteal:
        return '黄体期';
    }
  }

  @override
  String toString() {
    final df = DateFormat('yyyy-MM-dd');
    return '${dayIndex.toString().padLeft(2)} | ${df.format(date)} | raw=${rawPhaseLabel()} | disp=${displayPhaseLabel()}' +
        (overlappedByMenstruation ? ' (fertile 被月经覆盖)' : '');
  }
}

class CycleSummary {
  final DateTime periodStart;
  final DateTime nextPeriodStart;
  final DateTime ovulationDay;
  final DateTime fertilePreStartRaw;
  final DateTime fertilePreEndRaw;
  final DateTime fertilePostStartRaw;
  final DateTime fertilePostEndRaw;
  final Map<String, int> rawCounts;
  final Map<String, int> displayCounts;
  final int preTruncatedHeadDays;
  final int postTruncatedTailDays;

  CycleSummary({
    required this.periodStart,
    required this.nextPeriodStart,
    required this.ovulationDay,
    required this.fertilePreStartRaw,
    required this.fertilePreEndRaw,
    required this.fertilePostStartRaw,
    required this.fertilePostEndRaw,
    required this.rawCounts,
    required this.displayCounts,
    required this.preTruncatedHeadDays,
    required this.postTruncatedTailDays,
  });

  //用于打印所有的数据
  // String toPrettyString() {
  //   final df = DateFormat('yyyy-MM-dd');
  //   return '''本次月经开始: ${df.format(periodStart)}\n下次月经开始(推算): ${df.format(nextPeriodStart)}\n排卵日(推算): ${df.format(ovulationDay)}\n易孕前窗(理想原始): ${df.format(fertilePreStartRaw)} ~ ${df.format(fertilePreEndRaw)}\n易孕后窗(理想原始): ${df.format(fertilePostStartRaw)} ~ ${df.format(fertilePostEndRaw)}\n原始窗口计数: $rawCounts\n显示窗口计数: $displayCounts\n前窗在周期头部被截断天数: $preTruncatedHeadDays\n后窗在周期尾部被截断天数: $postTruncatedTailDays''';
  // }

  Map<String, int> compute() {
    return rawCounts;
  }
}

class CycleCalculator {
  final DateTime periodStart; // 本次月经开始日（周期起点）
  final int cycleLength; // 周期长度（天）
  final int periodLength; // 月经持续天数
  final int preFertileLength; // 易孕前窗长度（天），默认 5
  final int postFertileLength; // 易孕后窗长度（天），默认 4
  final int lutealLength; // 黄体期长度（天），用于推算排卵日，默认 14

  CycleCalculator({
    required this.periodStart,
    required this.cycleLength,
    required this.periodLength,
    this.preFertileLength = 5,
    this.postFertileLength = 4,
    this.lutealLength = 14,
  }) {
    if (cycleLength <= 0) throw ArgumentError('周期长度必须大于0');
    if (periodLength < 0 || periodLength > cycleLength) throw ArgumentError('月经长度必须在 0..周期长度 之间');
  }

  // ---- 辅助函数 ----
  static bool _sameDate(DateTime a, DateTime b) {
    return a.year == b.year && a.month == b.month && a.day == b.day;
  }

  static DateTime _dateOnly(DateTime d) => DateTime(d.year, d.month, d.day);

  static bool _inRangeInclusive(DateTime d, DateTime start, DateTime end) {
    final dd = _dateOnly(d);
    return !_dateOnly(start).isAfter(dd) && !_dateOnly(end).isBefore(dd);
  }

  // 生成周期内每一天的 raw/display 信息
  List<CycleDay> generateCycleDays() {
    final List<CycleDay> res = [];

    final nextPeriodStart = _dateOnly(periodStart.add(Duration(days: cycleLength)));
    final ovulationDay = _dateOnly(nextPeriodStart.subtract(Duration(days: lutealLength)));

    final fertilePreStart = _dateOnly(ovulationDay.subtract(Duration(days: preFertileLength)));
    final fertilePreEnd = _dateOnly(ovulationDay.subtract(Duration(days: 1)));

    final fertilePostStart = _dateOnly(ovulationDay.add(Duration(days: 1)));
    final fertilePostEnd = _dateOnly(ovulationDay.add(Duration(days: postFertileLength)));

    final menstruationStart = _dateOnly(periodStart);
    final menstruationEnd = _dateOnly(periodStart.add(Duration(days: periodLength - 1)));

    for (int i = 0; i < cycleLength; i++) {
      final day = _dateOnly(periodStart.add(Duration(days: i)));

      // 先判定 rawPhase（原始窗口类型）
      RawPhase raw;
      if (_inRangeInclusive(day, menstruationStart, menstruationEnd) && periodLength > 0) {
        raw = RawPhase.Menstruation;
      } else if (_sameDate(day, ovulationDay)) {
        raw = RawPhase.Ovulation;
      } else if (_inRangeInclusive(day, fertilePreStart, fertilePreEnd)) {
        raw = RawPhase.FertilePre;
      } else if (_inRangeInclusive(day, fertilePostStart, fertilePostEnd)) {
        raw = RawPhase.FertilePost;
      } else if (day.isBefore(ovulationDay)) {
        raw = RawPhase.Follicular;
      } else {
        raw = RawPhase.Luteal;
      }

      // 再判定 displayPhase（显示给用户的类型，遵循优先级）
      DisplayPhase disp;
      if (_inRangeInclusive(day, menstruationStart, menstruationEnd) && periodLength > 0) {
        disp = DisplayPhase.Menstruation;
      } else if (_inRangeInclusive(day, fertilePreStart, fertilePreEnd) || _inRangeInclusive(day, fertilePostStart, fertilePostEnd)) {
        disp = DisplayPhase.Fertile;
      } else if (_sameDate(day, ovulationDay)) {
        // 如果排卵日既在月经期，又在易孕期（理论少见），由于优先级声明，排卵日显示应为月经期。
        // 这里我们已经先检查了月经期，所以排卵日到这里代表不在月经期内。
        disp = DisplayPhase.Ovulation;
      } else if (day.isBefore(ovulationDay)) {
        disp = DisplayPhase.Follicular;
      } else {
        disp = DisplayPhase.Luteal;
      }

      final overlappedByMenses = (raw == RawPhase.FertilePre || raw == RawPhase.FertilePost) && disp == DisplayPhase.Menstruation;

      res.add(CycleDay(
        date: day,
        dayIndex: i + 1,
        rawPhase: raw,
        displayPhase: disp,
        rawTruncatedFromPrevCycle: fertilePreStart.isBefore(menstruationStart),
        rawTruncatedToNextCycle: fertilePostEnd.isAfter(nextPeriodStart.subtract(Duration(days: 1))),
        overlappedByMenstruation: overlappedByMenses,
      ));
    }

    return res;
  }

  // 生成汇总信息，包含原始/显示统计、原始窗口理想范围、截断/覆盖信息
  CycleSummary summarize() {
    final nextPeriodStart = _dateOnly(periodStart.add(Duration(days: cycleLength)));
    final ovulationDay = _dateOnly(nextPeriodStart.subtract(Duration(days: lutealLength)));

    final fertilePreStart = _dateOnly(ovulationDay.subtract(Duration(days: preFertileLength)));
    final fertilePreEnd = _dateOnly(ovulationDay.subtract(Duration(days: 1)));

    final fertilePostStart = _dateOnly(ovulationDay.add(Duration(days: 1)));
    final fertilePostEnd = _dateOnly(ovulationDay.add(Duration(days: postFertileLength)));

    final days = generateCycleDays();

    final Map<String, int> rawCounts = {
      '月经期': days.where((d) => d.rawPhase == RawPhase.Menstruation).length,
      '易孕前窗': days.where((d) => d.rawPhase == RawPhase.FertilePre).length,
      '排卵日': days.where((d) => d.rawPhase == RawPhase.Ovulation).length,
      '易孕后窗': days.where((d) => d.rawPhase == RawPhase.FertilePost).length,
      '卵泡期': days.where((d) => d.rawPhase == RawPhase.Follicular).length,
      '黄体期': days.where((d) => d.rawPhase == RawPhase.Luteal).length,
    };

    final Map<String, int> dispCounts = {
      '月经期': days.where((d) => d.displayPhase == DisplayPhase.Menstruation).length,
      '易孕期': days.where((d) => d.displayPhase == DisplayPhase.Fertile).length,
      '排卵日': days.where((d) => d.displayPhase == DisplayPhase.Ovulation).length,
      '卵泡期': days.where((d) => d.displayPhase == DisplayPhase.Follicular).length,
      '黄体期': days.where((d) => d.displayPhase == DisplayPhase.Luteal).length,
    };

    final preTruncatedHead = fertilePreStart.isBefore(_dateOnly(periodStart)) ? (_dateOnly(periodStart).difference(fertilePreStart).inDays) : 0;
    final postTruncatedTail = fertilePostEnd.isAfter(nextPeriodStart.subtract(Duration(days: 1))) ? (fertilePostEnd.difference(nextPeriodStart.subtract(Duration(days: 1))).inDays) : 0;

    return CycleSummary(
      periodStart: _dateOnly(periodStart),
      nextPeriodStart: nextPeriodStart,
      ovulationDay: ovulationDay,
      fertilePreStartRaw: fertilePreStart,
      fertilePreEndRaw: fertilePreEnd,
      fertilePostStartRaw: fertilePostStart,
      fertilePostEndRaw: fertilePostEnd,
      rawCounts: rawCounts,
      displayCounts: dispCounts,
      preTruncatedHeadDays: preTruncatedHead,
      postTruncatedTailDays: postTruncatedTail,
    );
  }
}


